﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Xml;
using cz.uhul.DSMLv2;


namespace cz.uhul.DSMLv2.Server.Sql
{



  public static partial class LdRequestServerExtension
  {





    public static List<Guid> Execute(this FilterOp Op, string DistinguishedName, LdSearchScopeEnum scope, bool optNegate)
    {

      if (Op is LogicalOp)
        return ((LogicalOp)Op).Execute(DistinguishedName, scope);

      else if(Op is NotOp)
        return  ((NotOp)Op).Execute(DistinguishedName, scope);

      else if (Op is AndOp)
        return  ((AndOp)Op).Execute(DistinguishedName, scope);

      else if (Op is OrOp)
        return  ((OrOp)Op).Execute(DistinguishedName, scope);

      else if (Op is EqualityMatchOp)
        return ((EqualityMatchOp)Op).Execute(DistinguishedName, scope, optNegate);

      else if (Op is SubstringsOp)
        return ((SubstringsOp)Op).Execute(DistinguishedName, scope, optNegate);

      else if (Op is PresentOp)
        return ((PresentOp)Op).Execute(DistinguishedName, scope, optNegate);

      else
        throw new InvalidSearchFilterException("unsupported operation: " + Op.OpTypeName);

    }







    public static List<Guid> Execute(this LogicalOp Op, string DistinguishedName, LdSearchScopeEnum scope)
    {

      //if (lOp.And != null)
      //  throw new InvalidSearchFilterException("nested 'and' condition error");

      //if (Op.Not != null)
      //  foreach(FilterOp op in Op.Not)
      //    if(op is NotOp || op is AndOp || op is OrOp)
      //      throw new InvalidSearchFilterException("resource limiter: operation 'not' on top of 'and', 'or' is not supported.");

      ObservableCollection<FilterOp> tmpAllOps = Op.Operations;

      if (tmpAllOps.Count < 2)
        throw new InvalidSearchFilterException("logical operation must have at least 2 operands");

      //get first operation result
      List<Guid> first = tmpAllOps[0].Execute(DistinguishedName, scope, false);
      List<Guid> second; //declare second

      for (int i = 1; i < tmpAllOps.Count; i++)
      {
        second = tmpAllOps[i].Execute(DistinguishedName, scope, false);

        if (Op is AndOp)
          first = AND(first, second);

        else
          first = OR(first, second);


      }

      return first;
    }




    private static List<Guid> AND(List<Guid> first, List<Guid> second)
    {
      return first.Where(p => second.Contains(p)).ToList<Guid>();
    }




    private static List<Guid> OR(List<Guid> first, List<Guid> second)
    {
      return first.Union(second).ToList<Guid>();
    }






    /// <summary>
    /// resolves not operation on top of equalityMatch, present or substrings operations only
    /// </summary>
    /// <param name="Op"></param>
    /// <param name="DistinguishedName"></param>
    /// <param name="scope"></param>
    /// <returns>list of found DnKey values from db</returns>
    public static List<Guid> Execute(this NotOp Op, string DistinguishedName, LdSearchScopeEnum scope)
    {
      if (Op.And != null || Op.Or != null || Op.Not != null)
        throw new InvalidSearchFilterException("resource limiter: operation 'not' on top of another 'and', 'or', 'not' is not supported");

      if(Op.EqualityMatch != null)
        return Op.EqualityMatch.Execute(DistinguishedName, scope, true);

      else if (Op.Present != null)
      {
        if(Op.Present.Name.ToLower() == "objectclass")
          throw new InvalidSearchFilterException("invalid filter: operation 'not' on top of 'present' operator with 'objectClass' attribute, is not supported");

        else
          return Op.Present.Execute(DistinguishedName, scope, true);
      }
      else
        throw new InvalidSearchFilterException("operation 'not' must have equalityMatch or present operation as child");
    }





    public static List<Guid> Execute(this EqualityMatchOp Op, string DistinguishedName, LdSearchScopeEnum scope, bool negate)
    {

      return LdDatabase.Search(scope.ToString(), "equalityMatch", DistinguishedName, Op.Name, Op.Value, null, negate);

    }




    public static List<Guid> Execute(this PresentOp Op, string DistinguishedName, LdSearchScopeEnum scope, bool negate)
    {

        return LdDatabase.Search(scope.ToString(), "present", DistinguishedName, Op.Name, null, null, negate);

    }





    public static List<Guid> Execute(this SubstringsOp Op, string DistinguishedName, LdSearchScopeEnum scope, bool negate)
    {

      return LdDatabase.Search(scope.ToString(), "substring", DistinguishedName, Op.Name, Op.Value, Op.Operation, negate);

    }




  }

}